Android View绘制是GUI系统的核心,而绘制view是需要缓冲区的,也就是我们说的画布,因此了解这个缓冲区的分配流程是有必要的,本篇将对该缓冲区的分配进行解释。
Activity结构剖析
Acitivty实际上在WMS端只是个Window,是以WindowState进行描述的,在AMS端它是一个ActivityRecord,在Activity内部它持有一个mWindow,它实际上为一个PhoneWindow实例,在PhoneWindow中维护了一个DecorView,也就是view树,这个PhoneWindow实例由一个WindowManagerImpl进行管理,在WindowManagaerImpl内部持有一个WindowManagerGlobal单例,它内部持有WMS的本地代理IWindowManager和由其打开的一个IWindowSession负责和WMS进行会话,另一方面它负责整个应用的窗口视图的管理,也就是DecorView及ViewRootImpl的管理。每个新添加的window最终会在WMS端以WidowState的形式存在于WMS,WMS不关心window的View树的内容,它只关心Window的大小样式以及Z序等,而绘制的流程是由我们应用程序进行的,实际上就是由ViewRootImp进行绘制的。那么相应的ViewRootImpl在进行绘制前是需要一个画布进行view树的绘制的,这个画布就是Surface。接下来我们将围绕这个Surface进行话题的展开。
画布的分配
在ViewRootImpl创建时同时会new一个Surface对象
1 | private final Surface mSurface = new Surface(); |
接下来我们看下这个Surface的内部会做些什么。
1 | public class Surface implements Parcelable { |
可以看到Surface的构造方法只是个空实现,而貌似mCanvas可能会是真正的画布,我们继续看看Canvas的代码
1 | public class Canvas { |
在Canava的构造方法中也并没有相关缓冲区分配的动作,这么说new出来的Surface并非真的分派了缓冲区,而只是一个空壳。想想也就能理解,我们知道ViewRootImpl分派的时机是WindowManagerGlobal通过addView来添加view树的时候分配的时候,这时候WMS还不知道这个Window的存在的(最起码得知道分配多大吧),那么给view树分配缓冲区的确为时过早。那么什么时候分配合适呢?当然是准备绘制view树的时候。view树的绘制是在performTraversal中进行的。
1 | private void performTraversals() { |
performTraversals函数比较长,我这里只取跟本篇相关的内容,在performTraversals并非仅仅只有三大绘制流程performMeasure,performLayout和performDraw,实际上在这三个流程之前还是做了很多事情的,我简单的说下:
-
计算期望的窗口大小,如果Window带状态栏,那么在其大小中应该除去状态栏的那部分宽高。否则大小应该为整个屏幕的大小
-
如果窗口大小发生了变化也需要记录下来
-
计算窗口的内容区域边衬是否变化
-
如果可见性发生了变化也需要记录,同时通知view树子视图可见性发生了变化
-
根据条件判断是否需要relayoutWindow,在relayoutWindow中会重新计算窗口大小。需要满足的条件至少为以下一种:
<1>. Activity窗口是第一次执行测量、布局和绘制操作,即ViewRoot类的成员变量mFirst的值等于true。 <2>. 前面得到的变量windowShouldResize的值等于true,即Activity窗口的大小的确是发生了变化。 <3>. 前面得到的变量insetsChanged的值等于true,即Activity窗口的内容区域边衬发生了变化。 <4>. Activity窗口的可见性发生了变化,即变量viewVisibilityChanged的值等于true。 <5>. Activity窗口的属性发生了变化,即变量params指向了一个WindowManager.LayoutParams对象
6.执行绘制流程,这个在另外的篇幅再做介绍
在relayoutWindow中会计算窗口的大小,同时这里会为窗口分配缓冲区。
1 | private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, |
通过mWindowSession和WMS进行会话调用relayout计算窗口的大小并将mSurface传递给WMS。IWindowSession的服务端为Session,我们看看它的实现
1 | public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, |
Session的relayout会通过WMS的relayoutWindow来完成。
1 | public int relayoutWindow(Session session, IWindow client, int seq, |
relayoutWindow的参树client为ViewRootImpl传递的W对象,是一个Binder对象,WMS通过client来通知应用窗口的变化。最后一个参数从命名上来看,它是一个出参,也就是通过这个方法可以拿到真正可用的Surface。可以看到outSurface是由copyFrom从SurfaceControl得来的。这个surfaceControl是通过winAnimator.createSurfaceLocked()来创建的。这个winAnimator是一个WindowStateAnimator,在我们为Window创建WindowState时创建的,它和WindowState是一一对应的。
1 | frameworks/base/services/java/com/android/server/wm/WindowStateAnimator.java |
mSurfaceControl在WindowStateAnimator中只会创建一次,这里的mSession实际上就是应用端和WMS进行会话的session,即IWindowSession,在创建时会指定缓冲区大小,格式以及标记。那么mSurfaceSession是什么呢,在哪里创建的呢?我们继续看
1 | frameworks/base/services/java/com/android/server/wm/WindowManagerService.java |
1 | frameworks/base/services/java/com/android/server/wm/WindowState.java |
1 | frameworks/base/services/java/com/android/server/wm/Session.java |
在WMS为应用打开一个会话session后并没有立马去创建mSurfaceSession,而是在Window添加到WMS的时候创建完WindowState后通过attach方法创建的。它同样的对于一个Session只会创建一个,也就是说我们的应用程序只会有一个SurfaceSession,因为它是Session的成员嘛。
1 | public final class SurfaceSession { |
SurfaceSession的实现很简单,因为它的工作都放在native层完成了。
1 | frameworks/base/core/jni/android_view_SurfaceSession.cpp |
实际上它是创建了一个SurfaceComposerClient对象并放在mNativeClient中。这个对象是用来和SurfaceFlinger打交道的,但它并不是SurfaceFlinger的本地代理。SurfaceFlinger负责界面图层的合成,这之间会涉及我们的缓冲区,所以分配缓冲区的工作需要经过SurfaceFlinger来进行。
弄清楚SurfaceSession是什么后,接下来我们看SurfaceControl的创建,它会用到SurfaceSession。
1 | public class SurfaceControl { |
同样的,它的工作也是在native层进行的。
1 | frameworks/base/core/jni/android_view_SurfaceControl.cpp |
1 | frameworks/base/core/jni/android_view_SurfaceSession.cpp |
在nativeCreate中首先通过android_view_SurfaceSession_getClient从SurfaceSession中取到之前创建的SurfaceComoserClient对象,然后通过该对象调用的CreateSurface创建SurfaceControl。
1 | /frameworks/native/libs/gui/SurfaceComposerClient.cpp |
SurfaceComposerClient在创建完后,强引用的会首先会执行onFirstRef 在这个方法里会通过
ComposerService::getComposerService获取到SurfaceFlinger的Binder本地代理sm,这是在ComposerService::connectLocked中通过getService获取的,SurfaceFlinger是实名Binder,因此可以通过ServiceManager查询得到。获取到SurfaceFlinger的本地代理后,就可以通过createConnection创建ISurfaceComposerClient,它是一个匿名Binder,是SurfaceFlinger服务中Client 的本地代理。Client也是个Binder Server,SurfaceFlinger为每个于其建立连接的应用进程维护一个Client便于管理。
1 | frameworks/native/services/surfaceflinger/Client.cpp |
在Client中createSurfac会通过请求参数构造一个MessageBase然后post到SurfaceFlinger的MessageQueue,最终事件会通过MessageBase的handleMessage进行处理,在handleMessage中调用了虚方法handler进行消息的处理。也就是MessageCreateLayer的handler进行处理。
1 | void MessageBase::handleMessage(const Message&) { |
在MessageCreateLayer的handler中又会通过SurfaceFlinger的createLayout为客户端创建一个Layer,这个Layer即图层,它对应于应用端的Window。
1 | status_t SurfaceFlinger::createLayer( |
在SurfaceFlinger的createLayer中会根据flag来创建不同的layer,分别为Normal和Dim类型的,创建完layer后会将其添加到client中去。这里我们看下CreateNormalLayer的实现即可。
1 | status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client, |
创建Layer的实例后就可以通过getBufferQueue获取到layer内部的BufferQueue,这个BufferQueue是个Binder Server,它是一个缓冲队列,用来维护layer的缓冲区。它是在Layer的onFirstRef中创建的,我们缓冲区的分配在SurfaceFligner端实际实际上就是通过它来分配的。同时,它既是缓冲区的消费者又是生产者,对于应用端来说,它扮演生产者的角色,因为应用端是填充数据的一方,而对于surfaceFligner来说它又扮演消费者的角色,因为SurfaceFlinger是合成layer层,取出缓冲区进行显示的。
1 | void Layer::onFirstRef() {//在第一次引用时创建一个bufferqueue |
这里的SurfaceTextureLayer它实际上就是个BufferQueue。
1 | sp<SurfaceControl> SurfaceComposerClient::createSurface( |
我们创建好layer后通过getBufferQueue后是以IGraphicBufferProducer的角色返回的,它代表了这个BufferQueue将作为一个生产者供应用端使用。紧接着在本地创建一个SurfaceControl
需要注意这个时候gbd是在我们的应用进程中了,它是BufferQueue的本地代理binder对象了。
我们通过这个代理对象来创建SurfaceControl。
1 | SurfaceControl::SurfaceControl( |
这里我们创建好本地的SurfaceControl,然后保存在java层的SurfaceControl的mNativeObject中。但是这个时候好像并没有真正去分配缓冲区,那么创建这个SurfaceControl给上层到底是干嘛用的?没错,就是用来分配缓冲区的,因为它内部这时候是有一个IGraphicBufferProducer的,它是layer中的BufferQueue代理binder。
在WMS的relayoutWindow中创建完SurfaceControl后,会通过Surface的copyFrom来初始化它,在这之前Surface还是个空壳。
1 | public class Surface implements Parcelable { |
java层的Surface是通过nativeCreateFromSurfaceControl,并将native层的SurfaceControl对象指针传递给它。
1 | static jint nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz, |
通过native层的SurfaceControl指针,可以通过getSurface获取到一个Surface。
1 | sp<Surface> SurfaceControl::getSurface() const//获取内部持有的Surface |
1 |
|
SurfaceControl 并不直接去分配缓冲区,而是通过管理一个本地Surface对象来管理缓冲区,它通过mGraphicBufferProducer构造,这个就是之前我们取到的IGraphicBufferProducer binder代理对象,构造的本地Surface最终会通过setNativeObjectLocked
保存在java层Surface的mNativeObject中。到这里java层的Surface就算创建完了,最终返回给ViewRootImpl使用。
Surface的使用
从Surface中取缓冲区
1 | private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, |
Surface首先通过lockCanvas获取到一个Canvas对象,这个Canvas实际上为就是通过Surface的缓冲区填充的画布。
1 | public Canvas lockCanvas(Rect inOutDirty) |
Surface的localCanvas是通过nativeLockCanvas,首先通过mNativeObject取到本地的Surface对象,通过这个本地的Surface进行lock取到缓冲区outBuffer,它是一个ANativeWindow_Buffer,随后通过这个缓冲区初始化一个SkBitmap画布,并设置其格式大小等,通过SkBitmap创建一个SkCanvas返回给java层Surface的mCanvas,我们在Draw方法中使用的Canvas就是这个SkCanvas.
1 | status_t Surface::lock( |
首先通过mGraphicBufferProducer通过dequeueBuffer分配缓冲区,实际是由BufferQueue完成的,它实际上是取到BufferQueue中BufferSlot槽的索引buf,有了这个索引,通过requestBuffer就真正的创建buffer并将该buffer映射到Surface所在进程。这样我们的应用程序就可以使用了。
提交绘制好的缓冲区
1 | public void unlockCanvasAndPost(Canvas canvas) { |
同样的应用层绘制完后,需要将该缓冲区unlock并且提交给BufferQueue。这个是通过nativeUnlockCanvasAndPost进行的。
1 | static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz, |
1 | status_t Surface::unlockAndPost()//提交Locked的Buffer |
Surfae的unlockAndPost调用queueBuffer方法完成buffer的提交
1 | int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {//buffer queue进行消费 |
通过mGraphicBufferProducer入队这个buffer,此时生产者的任务完毕,layer端的Consumer即SurfaceFlinger会监听到这个有效的缓冲区,然后准备合成显示layer,最终将绘制的东西显示在屏幕上。